// 参考： https://threejsfundamentals.org/threejs/lessons/threejs-post-processing.html
import { 
  AmbientLight,
  Color,
  Mesh,
  MeshPhongMaterial,
  PerspectiveCamera,
  Scene, 
  SphereGeometry, 
  SpotLight, 
  TextureLoader, 
  Vector2, 
  WebGLRenderer
} from "three";
import stats from '../utils/stats';
// 调用
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass";
import { FilmPass } from 'three/examples/jsm/postprocessing/FilmPass';
import { BloomPass } from 'three/examples/jsm/postprocessing/BloomPass';
import { DotScreenPass } from 'three/examples/jsm/postprocessing/DotScreenPass';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass';
import { TexturePass } from 'three/examples/jsm/postprocessing/TexturePass';
import { CopyShader } from 'three/examples/jsm/shaders/CopyShader'
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer";
import { GUI } from "dat.gui";

let scene = new Scene();
let renderer = setRenderer();
let camera = setCamera();

setLight();
let { composer, earth, filmComposer, bloomComposer, dotScreenComposer }= init();

const trackballControls = new OrbitControls(camera, renderer.domElement);
const halfWidth = window.innerWidth / 2;
const halfHeight = window.innerHeight / 2;
render();

function render() {
  stats.update();
  trackballControls.update();

  earth.rotation.y += 0.001;

  // 防止处理后的效果被清除
  renderer.autoClear = false;
  renderer.clear();

  renderer.setViewport(0, halfHeight, halfWidth, halfHeight);
  composer.render();

  renderer.setViewport(halfWidth, halfHeight, halfWidth, halfHeight);
  bloomComposer.render();

  renderer.setViewport(0, 0, halfWidth, halfHeight);
  dotScreenComposer.render();

  renderer.setViewport(halfWidth, 0, halfWidth, halfHeight);
  filmComposer.render();

  requestAnimationFrame(render);
}


function setRenderer() {
  const renderer = new WebGLRenderer({ antialias: true });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  // 支持阴影生成 
  renderer.shadowMap.enabled = true;
  document.body.appendChild(renderer.domElement);

  return renderer
}

function setCamera(): PerspectiveCamera {
  let camera = new PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);
  camera.position.set(0, 20, 40);
  scene.add( camera );

  return camera;
}

// 灯光设置
function setLight() {
  scene.add(new AmbientLight(0x444444));
  scene.add(new AmbientLight(0x343434));
  
  let spotLight = new SpotLight(0xffffff);
  spotLight.position.set(-10, 30, 40);
  spotLight.shadow.mapSize.width = 2048;
  spotLight.shadow.mapSize.height = 2048;
  spotLight.shadow.camera.fov = 15;
  spotLight.decay =2;
  spotLight.penumbra = 0.05;
  spotLight.castShadow = true;

  scene.add(spotLight);
  // scene.add(new SpotLightHelper(spotLight));
}

/** 功能区 */
function init() {
  let { earth } = addEarth();
  scene.add(earth);

  // 设置效果
  let renderPass = new RenderPass(scene, camera);
  
  // 原始效果 
  let composer = new EffectComposer(renderer);
  let effectCopy = new ShaderPass(CopyShader);
  effectCopy.renderToScreen = true;
  composer.addPass(renderPass);
  composer.addPass(effectCopy);
  
  let renderedScene = new TexturePass(composer.renderTarget2.texture);
  
  // 电视效果
  let filmComposer = new EffectComposer(renderer);
  let effectFilm = new FilmPass(0.8, 0.325, 256, 0);
  filmComposer.addPass(renderedScene);
  filmComposer.addPass(effectFilm);

  // 泛光效果
  let bloomPass = new BloomPass();
  let bloomComposer = new EffectComposer(renderer);
  bloomComposer.addPass(renderedScene);
  bloomComposer.addPass(bloomPass);
  bloomComposer.addPass(effectCopy);

  // 点集效果
  let dotScreenPass = new DotScreenPass();
  let dotScreenComposer = new EffectComposer(renderer);
  dotScreenComposer.addPass(renderedScene);
  dotScreenComposer.addPass(dotScreenPass);
  dotScreenComposer.addPass(effectCopy);

  addGUI(effectFilm, bloomPass, dotScreenPass);

  return { composer, filmComposer, bloomComposer, dotScreenComposer, effectCopy, earth };
}

function addGUI(obj: FilmPass, bloomPass: any, dotScreenPass: any ): void {
  const gui = new GUI();
  let controls = {
    grayScale: false,
    noiseIntensity: 0.8,
    scanlinesIntensity: 0.325,
    scanlinesCount: 256,

    strength: 3,
    kernelSize: 5.0,
    sigma: 5.0,
    resolution: 256,

    centerX: 0.5,
    centerY: 0.5,
    angle: 1,
    scale: 1
  }

  let updateBloom = () => {
    bloomComposer.passes[1] = new BloomPass(controls.strength, controls.kernelSize, controls.sigma, controls.resolution);
  }

  let film = gui.addFolder('电视效果');
  film.add(controls, 'grayScale').name('灰度').onChange(val => {
    (<any>obj.uniforms).grayscale.value = val;
  });
  film.add(controls, 'noiseIntensity', 0, 1, 0.01).name('颗粒度').onChange(val => {
    (<any>obj.uniforms).nIntensity.value = val;
  });
  film.add(controls, 'scanlinesIntensity', 0, 1, 0.01).name('扫描线程度').onChange(val => {
    (<any>obj.uniforms).sIntensity.value = val;
  });
  film.add(controls, 'scanlinesCount', 0, 500, 1).name('扫描线数量').onChange(val => {
    (<any>obj.uniforms).sCount.value = val;
  });

  let bloom = gui.addFolder('泛光效果');
  bloom.add(controls, 'strength', 0, 5, 0.01).name('泛光强度').onChange(val => {
    // bloomPass.copyUniforms.opacity.value = val;
    // bloomComposer.passes[1] = new BloomPass(val)
    updateBloom();
  });
  bloom.add(controls, 'kernelSize', 10, 100, 1).name('偏移量').onChange(updateBloom);
  bloom.add(controls, 'sigma', 1, 8, 0.01).name('税利程度').onChange(updateBloom);
  bloom.add(controls, 'resolution', 100, 256, 10).name('解析图').onChange(updateBloom);

  let dsFolder = gui.addFolder('点集效果');
  dsFolder.add(controls, 'centerX', 0, 5, 0.01).onChange(val => {
    dotScreenPass.uniforms.center.value.copy(new Vector2(val, controls.centerY));
  });
  dsFolder.add(controls, 'centerY', 0, 5, 0.01).onChange(val => {
    dotScreenPass.uniforms.center.value.copy(new Vector2(controls.centerX, val));
  });
  dsFolder.add(controls, 'angle', 0, 3.14, 0.01).name('角度').onChange(val => {
    dotScreenPass.uniforms.angle.value = val;
  });
  dsFolder.add(controls, 'scale', 0, 10, 1).name('缩放').onChange(val => {
    dotScreenPass.uniforms.scale.value = val;
  });
}

function addEarth(): { earth: Mesh } {
  let loader = new TextureLoader();
  let material = new MeshPhongMaterial({
    map: loader.load('/textures/earth/Earth.png'),
    normalMap: loader.load('/textures/earth/EarthNormal.png'),
    specularMap: loader.load('/textures/earth/EarthSpec.png'),
    specular: new Color(0x4444aa),
    normalScale: new Vector2(6, 6),
    shininess: 0.5
  });
  let earth = new Mesh(
    new SphereGeometry(13, 40, 40),
    material
  );
  
  return {
    earth
  }
}